package cc.rengu.redp.common.redis.impl;

import cc.rengu.redp.common.exception.RedisConnectException;
import cc.rengu.redp.common.function.JedisExecutor;
import cc.rengu.redp.common.redis.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;

import java.util.Arrays;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * Redis 工具类，只封装了几个常用的 redis 命令，
 * 可根据实际需要按类似的方式扩展即可。
 *
 * @author
 */
@Slf4j
@Service("redisService")
public class RedisServiceImpl implements RedisService {

    @Autowired
    RedisTemplate redisTemplate;

    private <T> T executeByRedisTemplate(JedisExecutor<RedisTemplate, T> j) throws RedisConnectException {
        try {
            return j.execute(redisTemplate);
        } catch (Exception e) {
            log.error("RedisTemplate执行失败", e);
            throw new RedisConnectException(e.getMessage());
        }
    }

    @Override
    public Set<String> getKeys(String pattern) throws RedisConnectException {
        return this.executeByRedisTemplate(j -> j.keys(pattern));
    }

    @Override
    public String get(String key) throws RedisConnectException {
        return this.executeByRedisTemplate(j -> {
            Object obj = j.opsForValue().get(key);
            if (!ObjectUtils.isEmpty(obj)) {
                return String.valueOf(obj);
            } else {
                return null;
            }
        });
    }

    @Override
    public String set(String key, String value) throws RedisConnectException {
        return this.executeByRedisTemplate(j -> {
            j.opsForValue().set(key, value);
            return "success";
        });
    }

    @Override
    public String set(String key, String value, Long milliscends) throws RedisConnectException {
        return this.executeByRedisTemplate(j -> {
            j.opsForValue().set(key, value, milliscends, TimeUnit.MILLISECONDS);
            return "success";
        });
    }


    @Override
    public Boolean setIfAbsent(String key, String value, Long milliscends) throws RedisConnectException {
        return this.executeByRedisTemplate(j -> {
            return j.opsForValue().setIfAbsent(key, value, milliscends, TimeUnit.MILLISECONDS);
        });
    }

    @Override
    public Long del(String... key) throws RedisConnectException {
        return this.executeByRedisTemplate(j -> j.delete(Arrays.asList(key)));
    }

    @Override
    public Boolean del(String key) throws RedisConnectException {
        return this.executeByRedisTemplate(j -> j.delete(key));
    }


    @Override
    public Boolean exists(String key) throws RedisConnectException {
        return this.executeByRedisTemplate(j -> j.hasKey(key));
    }

    @Override
    public Long pttl(String key) throws RedisConnectException {
        return this.executeByRedisTemplate(j -> j.getExpire(key, TimeUnit.MILLISECONDS));
    }

    @Override
    public Long pexpire(String key, Long milliseconds) throws RedisConnectException {
        return this.executeByRedisTemplate(j -> {
            j.expire(key, milliseconds, TimeUnit.MILLISECONDS);
            return milliseconds;
        });
    }

    @Override
    public Long zadd(String key, Double score, String member) throws RedisConnectException {
        return this.executeByRedisTemplate(j -> {
            if (j.opsForZSet().add(key, member, score)) {
                return 1L;
            } else {
                return null;
            }
        });
//        return this.excuteByJedis(j -> j.zadd(key, score, member));
    }


    private byte[] rawKey(Object key) {
        Assert.notNull(key, "non null key required");
        return this.redisTemplate.getKeySerializer() == null && key instanceof byte[] ? (byte[])((byte[])key) : this.redisTemplate.getKeySerializer().serialize(key);
    }

    @Override
    public Set<String> zrangeByScore(String key, String min, String max) throws RedisConnectException {
        return this.executeByRedisTemplate(j -> {
            double s = "-inf".equalsIgnoreCase(min) ? Double.MIN_VALUE : Double.valueOf(min);
            double e = "+inf".equalsIgnoreCase(max) ? Double.MAX_VALUE : Double.valueOf(max);
            Set set = j.opsForZSet().rangeByScore(key, s, e);
            return set;
        });
    }

    @Override
    public Long zremrangeByScore(String key, String start, String end) throws RedisConnectException {
        return this.executeByRedisTemplate(j -> {
            double s = "-inf".equalsIgnoreCase(start) ? Double.MIN_VALUE : Double.valueOf(start);
            double e = "+inf".equalsIgnoreCase(end) ? Double.MAX_VALUE : Double.valueOf(end);
            return j.opsForZSet().removeRangeByScore(key, s, e);
        });
    }

    @Override
    public Long zrem(String key, String... members) throws RedisConnectException {
        return this.executeByRedisTemplate(j -> j.opsForZSet().remove(key, members));
    }

    @Override
    public Long hset(String field, Map<String, String> hash) throws RedisConnectException {
        return this.executeByRedisTemplate(j -> {
            j.opsForHash().putAll(field, hash);
            return 1L;
        });
//        return this.excuteByJedis(j -> j.hset(field, hash));
    }

    @Override
    public Long hset(String field, String key ,String value) throws RedisConnectException {

        return this.executeByRedisTemplate(j -> {
            j.opsForHash().put(field, key,value);
            return 1L;
        });
//        return this.excuteByJedis(j -> j.hset(field, hash));
    }

    @Override
    public Map<String, String> hget(String field) throws RedisConnectException {
        return this.executeByRedisTemplate(j -> j.opsForHash().entries(field));
    }

    @Override
    public String hgetValue(String field, String key) throws RedisConnectException {
        return this.executeByRedisTemplate(j -> {
            if (!ObjectUtils.isEmpty(j.opsForHash().get(field, key))) {
                return String.valueOf(j.opsForHash().get(field, key));
            }
            return null;
        });
    }
}
